#ifndef _DATASTREAM_H_
#define _DATASTREAM_H_

#include "NetworkLibrary.h"
#include "TCharSupport.h"
#include "IDataReadable.h"
#include "IDataWritable.h"

#include <assert.h>

_BEGIN_NETWORK_NAMESPACE_


struct BufferIndicator{
	char * buffer;
	size_t length;
};

class InputDataStream{
public:
	InputDataStream( char * buffer, size_t length, bool needCopy = true )
		:m_copied( needCopy )
		,m_currentPlace( 0 ){

		if ( needCopy )
		{
			m_buffer.length = length;
			m_buffer.buffer = new char[length];
			memcpy( m_buffer.buffer, buffer, length );
		} 
		else
		{
			m_buffer.buffer = buffer;
			m_buffer.length = length;
		}
	}

	~InputDataStream( ){
		if ( m_copied ) delete[] m_buffer.buffer;
	}

	size_t GetSize() const{
		return m_buffer.length;
	}

	const char * GetBuffer() const{
		return m_buffer.buffer;
	}

	InputDataStream & operator>>( bool & data ){
		return this->ReadData( data );
	}

	InputDataStream & operator>>( char & data ){
		return this->ReadData( data );
	}

	InputDataStream & operator>>( unsigned char & data ){
		return this->ReadData( data );
	}

	InputDataStream & operator>>( int & data ){
		return this->ReadData( data );
	}

	InputDataStream & operator>>( unsigned int & data ){
		return this->ReadData( data );
	}

	InputDataStream & operator>>( long & data ){
		return this->ReadData( data );
	}

	InputDataStream & operator>>( unsigned long & data ){
		return this->ReadData( data );
	}

	InputDataStream & operator>>( float & data ){
		return this->ReadData( data );
	}

	InputDataStream & operator>>( double & data ){
		return this->ReadData( data );
	}

// 	InputDataStream & operator>>( tchar * & data ){
// 		assert( data );
// 
// 	}

	InputDataStream & operator>>( tstring & data ){
		tchar* start = ( tchar* ) ( m_buffer.buffer + m_currentPlace );
		size_t length = tstrlen( start );
		data = tstring( start );
		m_currentPlace += ( length + 1 ) * sizeof( tchar ) ;
		return ( * this );
	}

	InputDataStream & operator>>( IDataReadable & data ){
		data.ReadFromStream( *this );
		return ( * this );
	}

protected:

private:

	BufferIndicator m_buffer;
	bool			m_copied;
	size_t				m_currentPlace;

	template< typename T >
	InputDataStream & ReadData( T & data ){
		assert( m_currentPlace + sizeof( T ) <= m_buffer.length );
		
		data = * ( T * )( m_buffer.buffer + m_currentPlace );
		m_currentPlace += sizeof( T );
		return ( * this );
	}
};


class OutputDataStream
{
public:
	OutputDataStream( size_t capacity = DefaultCapacity );
	~OutputDataStream( );

	static const size_t					DefaultCapacity;

	size_t								GetCapacity() const;
	size_t								GetSize() const;
	const char *						GetBuffer() const;

	OutputDataStream &					operator<<( bool data );
	OutputDataStream &					operator<<( char data );
	OutputDataStream &					operator<<( unsigned char data );
	OutputDataStream &					operator<<( int data );
	OutputDataStream &					operator<<( unsigned int data );
	OutputDataStream &					operator<<( long data );
	OutputDataStream &					operator<<( unsigned long data );
	OutputDataStream &					operator<<( float data );
	OutputDataStream &					operator<<( double data );

	OutputDataStream &					operator<<( const tchar * str );
	OutputDataStream &					operator<<( const tstring & str );

	OutputDataStream &					operator<<( const IDataWritable & data );

	void								Write( const char * data, size_t size );

private:
										template< typename T >
	OutputDataStream &					WriteData( const T & data );

	void								ValidataBufferSize( size_t size );
	void								EnlargeBuffer( size_t size );

	size_t								m_capacity;
	char *								m_buffer;
	char *								m_curData;
};

template< typename T >
OutputDataStream & OutputDataStream::WriteData( const T & data )
{
	this->ValidataBufferSize( sizeof( T ) );

	T * curData = reinterpret_cast<T*>( m_curData );
	*curData = data;
	curData++;
	m_curData = reinterpret_cast<char*>( curData );
	return *this;
}

_END_NETWORK_NAMESPACE_


#endif